package dai.samples.batch.retry;

import dai.samples.batch.entity.BatchEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.json.JacksonJsonObjectMarshaller;
import org.springframework.batch.item.json.JacksonJsonObjectReader;
import org.springframework.batch.item.json.builder.JsonFileItemWriterBuilder;
import org.springframework.batch.item.json.builder.JsonItemReaderBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.retry.RetryException;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.transaction.PlatformTransactionManager;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;

/**
 * 任务重试
 * @author daify
 * @date 2020-11-03
 */
@Slf4j
@Configuration
public class RetryJobConfig {


    @Autowired
    JobBuilderFactory jobBuilderFactory;

    @Autowired
    StepBuilderFactory stepBuilderFactory;

    @Autowired
    PlatformTransactionManager transactionManager;

    @Bean("retryJob")
    public Job retryJob(JobRepository jobRepository) {
        return this.jobBuilderFactory.get("retryJob")
                .repository(jobRepository)
                .start(retryStep())
                .build();
    }

    @Bean("retrySkipJob")
    public Job retrySkipJob(JobRepository jobRepository) {
        return this.jobBuilderFactory.get("retrySkipJob")
                .repository(jobRepository)
                .start(retrySkipStep())
                .build();
    }

    @Bean("retryPolicyJob")
    public Job retryPolicyJob(JobRepository jobRepository) {
        return this.jobBuilderFactory.get("retryPolicyJob")
                .repository(jobRepository)
                .start(retrySkipStep())
                .build();
    }

    /**
     * 遇见RuntimeException异常进行重试，重试3次,使用retryPolicy重试
     * @return
     */
    @Bean("retryPolicy")
    public Step retryStepOption() {
        return this.stepBuilderFactory.get("retryPolicy")
                .<BatchEntity, BatchEntity>chunk(1)
                .reader(itemReader())
                .processor(getProcessor())
                .writer(itemWriter())
                .faultTolerant()
                .retryPolicy(getRetryPolicy())
                .build();
    }


    /**
     * 遇见RuntimeException异常进行重试，重试3次
     * @return
     */
    @Bean("retryStep")
    public Step retryStep() {
        return this.stepBuilderFactory.get("retryStep")
                .<BatchEntity, BatchEntity>chunk(1)
                .reader(itemReader())
                .processor(getProcessor())
                .writer(itemWriter())
                .faultTolerant()
                .retryLimit(3)
                .retry(RuntimeException.class)
                .noRetry(RetryException.class)
                .build();
    }

    /**
     * 遇见RuntimeException异常进行重试，重试3次
     * @return
     */
    @Bean("retrySkipStep")
    public Step retrySkipStep() {
        return this.stepBuilderFactory.get("retryStep")
                .<BatchEntity, BatchEntity>chunk(1)
                .reader(itemReader())
                .processor(getProcessor())
                .writer(itemWriter())
                .faultTolerant()
                .retryLimit(3)
                .retry(RuntimeException.class)
                .noRetry(RetryException.class)
                .skip(Exception.class)
                .skipLimit(100)
                .build();
    }

    /**
     * JSON写数据
     * @return
     */
    public ItemWriter<BatchEntity> itemWriter() {
        String name = "retryJob-" + System.currentTimeMillis();
        String patch = "target/test-outputs/" + name + ".json";
        return new JsonFileItemWriterBuilder<BatchEntity>()
                .jsonObjectMarshaller(new JacksonJsonObjectMarshaller<>())
                .resource(new FileSystemResource(patch))
                .name(name)
                .build();
    }

    /**
     * JSON读数据
     * @return
     */
    public ItemReader<BatchEntity> itemReader() {
        return new JsonItemReaderBuilder<BatchEntity>()
                .jsonObjectReader(new JacksonJsonObjectReader<>(BatchEntity.class))
                .resource(new ClassPathResource("data/batchJob.json"))
                .name("batchJobReader")
                .build();
    }

    public ItemProcessor<? super BatchEntity, ? extends BatchEntity> getProcessor() {
        return new ChangeNameProcessor();
    }


    /**
     * 处理过程
     */
    class ChangeNameProcessor implements ItemProcessor<BatchEntity, BatchEntity> {

        @Override
        public BatchEntity process(BatchEntity person) throws IOException {
            String fullName =person.getFirstName() + " " +
                    person.getLastName();
            person.setFullName(fullName);
            System.out.println(fullName + ":" + person.getAge());
            if (person.getAge() > 10) {
                throw new RuntimeException("123");
            }
            return person;
        }
    }

    /**
     * 简单的重试策略
     * @return
     */
    public RetryPolicy getRetryPolicy() {
        // 重试的异常集合
        Map<Class<? extends Throwable>, Boolean> classBooleanMap =
                Collections.singletonMap(Exception.class, true);
        // 参数为： 重试次数、重试的异常集合
        return new SimpleRetryPolicy(3, classBooleanMap);
    }
}
